# coding=utf-8
import random
import copy
from utils.zentao import ZenTaoBusiness
from utils.zentao_case import ZenTaoCase


class CaseData(object):
    """数据清洗"""

    def __init__(self, content, path, import_type):
        self.path = path
        self.import_type = import_type
        self.story = 'story'
        self.name = '产品名称'
        self.lib = 'product'
        self.object = ZenTaoCase
        self.get_object()
        self.title, self.first_key_word, self.first_story = self.object.key_word(content)
        self.r_session = ZenTaoBusiness.get_session()
        self.object.zt_new_login(self.r_session)
        self.library_id = self.check_library()
        self.zt_case = []
        self.all_title = []
        self.repeat_case_title = []
        self.all_modules_name = {}
        self.module = {}
        self.condition = ['前置条件:', '前置条件：', '前提条件:', '前提条件：']
        self.field = ['前置条件', '关键词', '需求']
        self.errors = []

    def get_object(self):
        """根据导入类型选择不同的类
        :return:
        """
        # 1 是默认父类，2是产品用例模块的子类
        if self.import_type == '1':
            self.story = ''
            self.name = '用例库名称'
            self.lib = 'lib'
            self.object = ZenTaoBusiness

    def check_library(self):
        """校验用例库名称或者产品名称是否存在
        :return:
        :return: 用例库id
        """
        library_id = self.object.according_name_find_ib(self.title, self.r_session)
        if library_id is None:
            raise Exception('{}不存在:{}'.format(self.name, self.title))
        return library_id

    def get_modules(self, node):
        """获取用例名称对应的分组名称
        :param node: 存储一级标题、二级标题的列表
        :return:
        """
        # 获取所有模块节点名称-id唯一，title可能重复
        all_modules_name = {n.get('first_id'): n.get('first_title') for n in node}

        modules_value = all_modules_name.keys()
        # 筛选用例数据
        case_modules = [n_case for n_case in node if
                        n_case.get('id') not in modules_value]
        # 根据id去找上级名称，用/拼接所有模块名称组成模块名
        for case in case_modules:
            first_title = case.get('first_title')
            first_id = case.get('first_id')
            note = case.get('note')
            all_modules = self.get_node_name(first_title, first_id, node, [case.get('first_title')])
            case['first_title'] = self.handle_node_name(all_modules)
            # BYT根据id去找到上级的前置条件，用\r\n拼接所有的前置条件
            all_note = self.get_note(first_title, first_id, node, note)
            case['note'] = all_note
            # BYT修改用例的标题，标题=二级以下到标题的名称-标题名
            first_title = '-'.join(case['first_title'][1:].split('/')[1:])
            case['title'] = '{}-{}'.format(first_title, case['title'])
        return case_modules

    @staticmethod
    def handle_node_name(node_list):
        """拼接处理节点名称
        :param node_list:所有层级
        :return:拼接为str
        """
        # 数组倒序
        node_list.reverse()
        if node_list[0][0] == '/':
            node_name = '/'.join([node.strip() for node in node_list])
        else:
            node_name = '/' + '/'.join([node.strip() for node in node_list])
        return node_name

    def handle_node(self, data, node):
        """处理用例模块标题
        :param data: 去掉画布的XMind数据
        :param node: 存储一级标题、二级标题的列表
        """
        # 计算2级标签
        for d in data:
            first_title = d.get('title')
            first_id = d.get('id')
            topics = d.get('topics')
            # 有优先级，忽略后面节点
            if d.get('markers'):
                continue
            if topics:
                # 有前置条件，忽略后面节点
                if topics[0].get('title').find('前置条件:') != -1 \
                        or topics[0].get('title').find('前置条件：') != -1:
                    continue
                num = len(topics)
                for n in range(num):
                    title = topics[n].get('title')
                    id = topics[n].get('id')
                    #BYT获取当前节点的备注，NONE转换成空
                    note = topics[n].get('note')
                    if not note:
                        note=''
                    node.append({'first_title': first_title, 'first_id': first_id,
                                 'title': title, 'id': id, 'note': note})  # BYT 新增 'note': note获取备注信息
            if topics is not None:
                self.handle_node(topics, node)
        return node

    @classmethod
    def get_node_name(cls, first_title, first_id, node, node_modules):
        """获取用例上级所有节点名称
        :param first_title:当前层级的名称
        :param first_id:
        :param node:存储一级标题、二级标题的列表
        :param node_modules:当前层级上级名称-list
        :return: 所有上级的层级放在一个list
        """
        while True:
            super_title, first_id = cls.get_super_title(node, first_title, first_id)
            if super_title is None:
                return node_modules
            else:
                node_modules.append(super_title)
                first_title = super_title

    @classmethod
    def get_super_title(cls, data, title, first_id):
        """获取上级标题：模块节点
        :param data: 所有数据
        :param title: 标题
        :param first_id:
        :return:没有返回None，有上级返回上级名称
        """
        for d in data:
            if title == d.get('title') and first_id == d.get('id'):
                return d.get('first_title'), d.get('first_id')
        return None, first_id

    @classmethod
    def get_note(cls, first_title, first_id, node, note):
        """
        BYT新增
    获取用例上级所有节点的备注
        :param first_title: 上节点的标题
        :param first_id: 上节点id
        :param node: 存储一级标题、二级标题的列表
        :param note: 当前节点的备注
        :return: 返回当前节点+所有上级节点的备注
        """
        while True:
            super_note, super_title, first_id = cls.get_super_note(node, first_title, first_id)
            if super_title is None:
                return note
            else:
                first_title = super_title
                if super_note == None:
                    continue
                if note:
                    note = f'{super_note}\r\n{note}'
                else:
                    note = f'{super_note}'

    @classmethod
    def get_super_note(cls, data, first_title, first_id):
        """
        获取上级标题：模块节点
        :param data:存储一级标题、二级标题的列表
        :param first_title:当前节点的标题
        :param first_id:当前节点id
        :return:当前节点的备注 \上节点的title\上节点的id，

        """

        for d in data:
            if first_title == d.get('title') and first_id == d.get('id'):
                return d.get('note'), d.get('first_title'), d.get('first_id')
        return d.get('note'), None, first_id

    def get_case_result(self, case_data):
        """获取用例结果，或者为步骤
        :param case_data: 用例下的数据
        :return:
        """
        case_result = []
        for case in case_data:
            step = case.get('title')
            two_result = case.get('topics')
            # 结果为空,且不是分组
            if not two_result:
                case_result.append({'step': step, 'expect': '', 'is_group': False})
                continue
            # 如果只有下级，则为步骤，如果有3级，则是分组
            is_group = self.judge_group(two_result)
            # 不是分组
            if not is_group:
                # 结果为空
                if not two_result:
                    case_result.append({'step': step, 'expect': '', 'is_group': False})
                else:
                    case_result.append(
                        {'step': step, 'expect': two_result[0].get('title'), 'is_group': False})
            else:
                group = {'group_name': step, 'step_expect': [], 'is_group': True}
                for step_expect in two_result:
                    expect = step_expect.get('title')
                    if step_expect.get('topics'):
                        group.get('step_expect').append(
                            {'step': expect, 'expect': step_expect.get('topics')[0].get('title')})
                    else:
                        # 分组中无预期结果
                        group.get('step_expect').append({'step': expect, 'expect': ''})
                case_result.append(group)
        return case_result

    @staticmethod
    def judge_group(result):
        """判断当前步骤结果是否为3级，如果是，则为分组
        :param result:
        :return:
        """
        if not result:
            return False
        for line in result:
            if line.get('topics'):
                return True
        return False

    @classmethod
    def handle_pri(cls, pri):
        """获取用例优先级
        :param pri:用例打标
        :return: 用例优先级
        """
        if pri is not None:
            case_pri = str(pri).split('-')[1]
            # 如果为数字
            if len(case_pri) == 1:
                # 如果优先级大于4置为4
                if int(case_pri) >= 4:
                    return 4
                else:
                    return case_pri
            # 如果为其他字符,默认为3
            else:
                return 3
        # 没有设置优先级,默认为3
        else:
            return 3

    def get_all_case(self, topic_s, tree_id=None, super_title=None, pri=None):
        """返回所有用例的内容
        :param topic_s:x_mind的数据
        :param tree_id:当前节点id
        :param super_title:用例标题
        :param pri:用例优先级
        :return: 所有用例的内容列表
        """
        # 遍历树
        for TOPIC in topic_s:
            # 如果没有下级
            if not TOPIC.get('topics'):
                if TOPIC.get('markers'):
                    case_step = self.handle_markers(TOPIC, tree_id, super_title, pri)
                    if not case_step:
                        break
                    continue
                # 如果当前节点为前置条件
                elif set([TOPIC.get('title').find(c) for c in self.condition]) != {-1}:
                    self.handle_markers(topic_s, tree_id, super_title, pri, False)
                    # 用例内容处理完重新遍历上级节点
                    break
            else:
                # 如果勾选用例优先级
                if TOPIC.get('markers'):
                    # 如果标记❌,忽略下面所有用例
                    if TOPIC.get('markers') == ['symbol-wrong']:
                        continue
                    case_step = self.handle_markers(TOPIC, tree_id, super_title, pri)
                    if not case_step:
                        break
                    continue
                else:
                    # 当前节点的id,用于后面用例标题找上级模块id
                    tree_id = TOPIC.get('id')
                self.get_all_case(TOPIC.get('topics'), tree_id, TOPIC.get('title'), pri)

    def handle_markers(self, topic, tree_id, super_title, case_pri, is_pre=True):
        self.return_ = """
        :param topic:优先级的节点数据
        :param tree_id:节点id
        :param super_title:用例标题
        :param case_pri:优先级
        :param is_pre:是否为前置条件
        :return:
        """
        if is_pre:
            case_pri = topic.get('markers')[0]
            tree_id = topic.get('id')
            # super_title =topic.get('title')
            super_title = [x['title'] for x in self.all_modules_name if x['id'] == tree_id][0]  # BYT新增：获取的标题加上所有上级的名称
            print(super_title)
            topic = topic.get('topics')
        # 处理用例
        case_step = self.handle_case(topic, tree_id, super_title, case_pri)
        print('用例内容:{}'.format(case_step))
        self.zt_case.append(case_step)
        return case_step

    def according_case_title_find_modules_id(self, case_title, tree_id):
        """根据用例标题获取模块id
        :param case_title:用例标题
        :param tree_id:树节点
        :return: modules_id
        """
        for modules_name in self.all_modules_name:
            if case_title == modules_name.get('title') and tree_id == modules_name.get('id'):
                case_modules_name = modules_name.get('first_title')
                modules_id = self.get_modules_id(case_modules_name, modules_name)
                return modules_id
        # 异常场景
        return None

    def get_modules_id(self, case_modules_name, modules_name):
        """根据导入类型不同获取不同的模块-self.object
        :param case_modules_name:
        :param modules_name:
        :return:
        """
        modules_id = self.object.according_module_name_find_id(case_modules_name,
                                                               self.module,
                                                               self.r_session,
                                                               self.library_id)
        # 如果没有找到模块名,添加模块,并返回模块id
        if modules_id is None:
            modules_id = self.object.add_modules(self, self.r_session,
                                                 modules_name.get('first_title'),
                                                 self.library_id)
            self.module[modules_id] = case_modules_name
        return modules_id

    def handle_case(self, contents, tree_id, super_title, case_pri):
        """xMind处理用例
        :param contents: xMind内容
        :param super_title: 用例标题
        :param case_pri: 用例优先级
        :param tree_id: 树节点id
        :return:
        """
        try:
            # 获取前置条件、关键词等
            self.get_precondition_key_word(contents)
            # BYT新增，前置条件添加备注中的内容
            tree_precondition = [one['note'] for one in self.all_modules_name if one['id'] == tree_id][0]
            self.precondition = f'{tree_precondition}\r\n{self.precondition}'
            case_set = {
                'precondition': self.precondition,
                'keywords': self.key_word,
                'title': self.repeat_title(super_title, tree_id),
                '{}'.format(self.lib): self.library_id,
                'module': self.according_case_title_find_modules_id(super_title, tree_id),
                'pri': CaseData.handle_pri(case_pri),
                'type': 'feature'
            }
            # case_set['title'] = self.repeat_title(super_title, tree_id)
            if self.story:
                case_set['story'] = self.story
            if not contents:
                return case_set
            # 清洗用例数据
            case_result = self.get_case_result(contents)
            # 处理用例步骤
            case_set = self.handle_case_step(case_result, case_set)
            return case_set
        except Exception as e:
            print('用例:{}有误,请检查用例步骤结果,错误信息:{}'.format(super_title, str(e)))
            self.errors.append(super_title)
            raise Exception('用例:{}有误,请检查用例步骤结果'.format(super_title))

    def get_precondition_key_word(self, contents):
        """获取前置条件、关键词
        :param contents:
        :return:precondition, key_word
        """
        # 替换全局变量
        self.story = self.first_story
        self.key_word = self.first_key_word
        # 只有用例标题,后面节点为空
        if not contents:
            self.precondition = ''
            return
        # 处理第一行是否有内容
        all_content = contents[0].get('title')
        base_field = [f for f in self.field if all_content.find(f) != -1]

        # 无前置条件等字段

        if not base_field:
            self.precondition = ''
            return
        base_field.reverse()

        # for b in base_field:


        for b in base_field:
            # 替换局部需求id

            if b == self.field[-1]:
                try:
                    self.story = int(ZenTaoCase.handle_colon(all_content, b))
                except ValueError:
                    raise Exception('需求ID只能为数字')
            # 替换局部关键词
            elif b == self.field[-2]:
                self.key_word = ZenTaoCase.handle_colon(all_content, b)
            else:
                self.precondition = ZenTaoCase.handle_colon(all_content, b)
            all_content = all_content.split(b)[0]
        del contents[0]

    def repeat_title(self, title, tree_id):
        """重复用例标题处理
        :param title:
        :param tree_id:
        :return:
        """
        # 用例标题重复，上级模块名+用例名组成新的用例标题
        if title in self.all_title:
            name = [name.get('first_title') for name in self.all_modules_name if
                    name.get('id') == tree_id]
            print('重名的用例标题：{}'.format(title))
            title = '{}-{}'.format(name[0].split('/')[-1], title)
            # 如果新的用例标题还重复,+随机值
            if title not in self.repeat_case_title:
                self.repeat_case_title.append(title)
            else:
                title = '{}-重复{}'.format(title, random.randint(0, 999))
        else:
            self.all_title.append(title)
        return title

    @staticmethod
    def handle_case_step(case_result, case_set):
        """处理用例->禅道可接受的用例
        :param case_result:
        :param case_set:
        """
        for s in range(1, len(case_result) + 1):
            case_line = case_result[s - 1]
            # 如果为分组,is_group=True
            if case_line.get('is_group'):
                case_set['steps[{}]'.format(s)] = case_line.get('group_name')
                case_set['stepType[{}]'.format(s)] = 'group'
                case_set['expects[{}]'.format(s)] = ''
                step_expects = case_line.get('step_expect')
                for index in range(len(step_expects)):
                    step_expect = step_expects[index]
                    case_set['steps[{}.{}]'.format(s, index + 1)] = step_expect.get('step')
                    case_set['stepType[{}.{}]'.format(s, index + 1)] = 'item'
                    case_set['expects[{}.{}]'.format(s, index + 1)] = step_expect.get('expect')
            else:
                # 只有步骤、结果
                case_set['steps[{}]'.format(s)] = case_line.get('step')
                case_set['stepType[{}]'.format(s)] = 'step'
                case_set['expects[{}]'.format(s)] = case_line.get('expect')
        return case_set

    def get_case_data(self, topics):
        """获取用例数据
        :param topics:文件内容
        :return:
        """
        # 获取用例所有上级节点名称
        all_node = self.handle_node(topics, [])
        # 拼接模块名称
        self.all_modules_name = self.get_modules(all_node)
        # 获取禅道用例库下所有模块名称
        self.module = self.object.get_all_module_id(self.library_id, self.r_session)
        self.get_all_case(topics)
        return self

    @classmethod
    def if_exist_cases(cls, case_data, path):
        """判断是否存在用例
        :param case_data:
        :param path:
        :return:
        """
        for c_data in case_data:
            if c_data.get(path):
                return c_data
        return False

    def sync_zt_case(self):
        """同步用例
        :return:
        """
        try:
            # 获取禅道添加用例前置数据
            zt_data = ZenTaoBusiness.get_zt_case_add_front_data(self.import_type, self.library_id)
            input_case=[]
            for index, case in enumerate(self.zt_case, 1):
                self.object.new_zt_case_sync(self.r_session, case, zt_data)
                case_desc = 'num:{} 用例标题:{}'.format(index, case.get('title'))
                input_case.append(case_desc)
            return input_case
        except Exception as error:
            print(str(error))
